#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>

#include "server_type.h"
#include "platform.h"
#include "socketlibfunction.h"
#include "epollserver.h"

struct epoll_server_s
{
    struct server_s base_server;
    int epoll_fd;
};

static void epoll_del_event(struct epoll_server_s* self, sock fd)
{
    //epoll_event ev = { 0, { 0 } };
    //ev.data.fd = AFD;
    //ev.data.ptr = NULL;

    epoll_ctl(self->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
}


static void epoll_add_event(struct epoll_server_s* self, sock fd, struct session_s* client, uint32_t events)
{
    struct epoll_event ev = { 0, { 0 } };
    ev.events = events;

    ev.data.fd = fd;
    ev.data.ptr = client;
    epoll_ctl(self->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
}

static void epoll_modify_event(struct epoll_server_s* self, sock fd, struct session_s* client, uint32_t events)
{
    struct epoll_event ev = { 0, { 0 } };

    ev.data.fd = fd;
    ev.data.ptr = client;
    ev.events = events;

    epoll_ctl(self->epoll_fd, EPOLL_CTL_MOD, fd, &ev);
}

static void epoll_server_halfclose(struct epoll_server_s* self, struct session_s* session)
{
    if(SOCKET_ERROR != session->fd && SOCKET_CONNECTED == session->status)
    {
        sock fd = session->fd;
        epoll_del_event(self, fd);
        shutdown(fd, 2);
        socket_close(fd);

        server_remove_readable(&self->base_server, session);
        server_remove_writable(&self->base_server, session);

        server_disable_session_writable(&self->base_server, session);
        server_disable_session_writable(&self->base_server, session);

        session->fd = SOCKET_ERROR;
    }
}

static void epoll_server_close(struct server_s* self, struct session_s* session)
{
    struct epoll_server_s* epoll_server = (struct epoll_server_s*)(self);
    epoll_server_halfclose(epoll_server, session);
    server_socket_onclose(self, session);
}

static void epoll_server_enter(struct server_s* self, struct session_s* session)
{
    struct epoll_server_s* epoll_server = (struct epoll_server_s*)(self);
    server_enable_session_writable(self, session);
    epoll_add_event(epoll_server, session->fd, session, EPOLLET | EPOLLIN | EPOLLOUT | EPOLLRDHUP);
}

static bool epoll_server_recv(struct server_s* self, struct session_s* session)
{
    struct epoll_server_s* epoll_server = (struct epoll_server_s*)(self);
    struct buffer_s* buffer = session->recv_buffer;
    bool del = false;
    int recv_result = session_recv(session);

    if(recv_result & RECV_OK)
    {
        int total_len = buffer->write_pos-buffer->read_pos;
        int process_len = self->logic_on_recved(self, session->index, buffer->data+buffer->read_pos, total_len);

        if(process_len > 0 && process_len <= total_len)
        {
            buffer->read_pos += process_len;
        }
    }
    
    if(recv_result & RECV_CLOSE)
    {
        epoll_server_close(self, session);
        self->logic_on_close(self, session->index);
    }
    else
    {
        if(recv_result & RECV_EAGIN)
        {
            server_disable_session_readable(self, session);
            del = true;
        }
    }

    return del;
}

static bool epoll_server_send(struct server_s* self, struct session_s* session)
{
    struct epoll_server_s* epoll_server = (struct epoll_server_s*)(self);
    bool del = false;
    int send_result = session_send(session);

    if(send_result == SEND_CLOSE)
    {
        epoll_server_close(self, session);
        self->logic_on_close(self, session->index);
    }
    else
    {
        if(send_result & SEND_EAGIN)
        {
            server_disable_session_readable(self, session);
            del = true;
        }
    }

    return del;
}

static void epoll_server_poll(struct server_s* self)
{
    int i = 0;
    struct epoll_server_s* epoll_server = (struct epoll_server_s*)(self);
    struct epoll_event events[128];
    uint32_t event_data;
    int nfds = epoll_wait(epoll_server->epoll_fd, events, sizeof(events) / sizeof(struct epoll_event), 0);

    for(; i < nfds; ++i)
    {
        struct session_s*   client = (struct session_s*)(events[i].data.ptr);

        if(NULL != client)
        {
            event_data = events[i].events;

            if(event_data & EPOLLRDHUP)
            {
                epoll_server_recv(self, client);
            }
            else
            {
                if(event_data & EPOLLIN)
                {
                    server_enable_session_readable(self, client);
                }

                if(event_data & EPOLLOUT)
                {
                    server_enable_session_writable(self, client);
                }
            }
        }
    }
}

static void epoll_server_handler_io(struct server_s* self)
{
    server_handler_recv(self);
    server_handler_send(self);
}

struct server_s* epollserver_new(int num, logic_on_enter_pt logic_on_enter, logic_on_close_pt logic_on_close, logic_on_recved_pt logic_on_recved, int recv_buffer_size, int send_buffer_size)
{
    struct server_s* base_server_p = NULL;
    struct epoll_server_s* epoll_server_p = (struct epoll_server_s*)malloc(sizeof(struct epoll_server_s));

    if(NULL != epoll_server_p)
    {
        memset(epoll_server_p, 0, sizeof(*epoll_server_p));
        
        if(-1 != (epoll_server_p->epoll_fd = epoll_create(256)))
        {
            base_server_p = &epoll_server_p->base_server;
            server_init(base_server_p, num, recv_buffer_size, send_buffer_size);
            if(NULL == base_server_p)
            {
                break;
            }

            base_server_p->poll = epoll_server_poll;
            base_server_p->handler_io = epoll_server_handler_io;
            base_server_p->recv = epoll_server_recv;
            base_server_p->send = epoll_server_send;
            base_server_p->close = epoll_server_close;
            base_server_p->enter = epoll_server_enter;

            base_server_p->logic_on_enter = logic_on_enter;
            base_server_p->logic_on_close = logic_on_close;
            base_server_p->logic_on_recved = logic_on_recved;
        }
        else
        {
            epollserver_delete(base_server_p);
            base_server_p = NULL;
        }
    }

    return base_server_p;
}

void epollserver_delete(struct server_s* self)
{
    if(NULL != self)
    {
        struct epoll_server_s* epoll_server = (struct epoll_server_s*)(self);
        server_destroy(self);

        if(epoll_server->epoll_fd >= 0)
        {
            close(epoll_server->epoll_fd);
            epoll_server->epoll_fd = -1;
        }

        free(epoll_server);
        epoll_server = NULL;
    }
}